/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * callbacks.c
 * Copyright (C) The Pennsylvania State University 2008 <tpk5010@psu.edu>
 * 
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include <string.h>
#include "callbacks.h"
#include "streaming.h"
#include <curl/curl.h>
#include <gps.h>

/* Function:	getHeaders
 * Return:		size_t
 * Arguments:	void*, size_t, size_t, void*
 *
 * This function conforms to the libcurl documentation for a CURLOPT_HEADERFUNCTION.
 * Realistically, it is called by curl for every line in the header response to a
 * curl_easy_perform. See the libcurl docs for more details.
 */
size_t getHeaders(void *ptr, size_t size, size_t nmemb, void *data) {
	int bytes = size * nmemb;
	AppUIData* uidata = (AppUIData*)data;
	
	/* If the header we're working on is u-Can-Haz-Deez-Ports, create a char* buffer of 
	 * length 5 (4 characters, 1 NULL character), copy the video port to the buffer, and
	 * use the buffer to populate the uidata->data->vstream->vport gint
	 */
	if(!strncmp((char*)ptr, "u-Can-Haz-Deez-Portz: ", 22)) {
		char *buffer=(char*)malloc(5);
		strncpy(buffer, (char*)ptr+22, 4);
		buffer[5]='\0';
		uidata->data->vstream->vport = atoi(buffer);
	}
	
	return bytes;
}

/* Function: discardResp
 * Return: size_t
 * Arguments: void*, size_t, size_t, void*
 *
 * This function conforms to the libcurl documentation for a CURLOPT_WRITEFUNCTION.
 * Because we don't care about the data returned by the request, we simply discard
 * the data.
 */
 size_t discardResp(void* ptr, size_t size, size_t nmemb, void *data) {
 	int bytes = size * nmemb;
 	return bytes;
 }
 
/* Function: gps_callback
 * Return: void
 * Arguments: gps_data_t*, char*, size_t, int
 *
 * This function is called by the gps thread, everytime there is new info from the
 * gps. I'm not sure about the particulars for this function yet; we'll need to 
 * consult the libgps docs.
 * 
 * As of now, this does not correctly update the interface, or implement proper
 * error handling.
 */
void gps_callback(struct gps_data_t *gps, char *buf, size_t len, int level) {
	if(gps->fix.mode == MODE_NOT_SEEN) {
		return;
	}
	printf("%f N %f E (fix: %d)\n", gps->fix.latitude, gps->fix.longitude, gps->fix.mode);
}

/* Function:	submitReport
 * Return:		void
 * Arguments:	gpointer
 * 
 * submitReport is the function that actually communicates with the server.
 * This is called from createReport and is executed in its own thread. See 
 * createReport function.
 *
 */
void submitReport(gpointer thread_data) {
	AppUIData* uidata = thread_data;
	
	logit("Preparing to submit report.\n", GTK_TEXT_VIEW(uidata->logview));
	
	/* submitData holds an HTTP GET style rendition of our form options */
	/* The keys correspond to the form names on the webform on the backend */

	gchar *submitData = "deviceID=";
	char *escapeThis = "";
	char *url = "";
	GtkTextIter startIter, endIter;
	gchar sev[3];
	gchar devid[5];
	gchar gpslat[10];
	gchar gpslong[10];
	GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(uidata->detailsview));
	
	gtk_text_buffer_get_start_iter(buffer, &startIter);
	gtk_text_buffer_get_end_iter(buffer, &endIter);
	g_ascii_dtostr(sev, 3, (gdouble)gtk_combo_box_get_active(GTK_COMBO_BOX(uidata->severitycombo)));
	
	g_ascii_dtostr(devid, 5, (gdouble)uidata->data->config->DeviceID);
	
	/* Construct the submit URL using the server from the config file. */
	url = g_strconcat(url, "http://", (gchar *)uidata->data->config->Server, "/eel/submit.php", NULL);

	/* Get the DeviceID from the config file. */
	submitData = g_strconcat(submitData, devid, "&eventType=", NULL);

	/* curl_easy_escape makes our strings all cool and also awesomely escaped for http. */
	/* Concat eventType and eventSeverity. If there's no type we assume there's no severity. */
	if((char *)gtk_combo_box_get_active_text(GTK_COMBO_BOX(uidata->categorycombo)) != NULL) {
		escapeThis = curl_easy_escape(uidata->data->curl, (char *)gtk_combo_box_get_active_text(GTK_COMBO_BOX(uidata->categorycombo)), 0);
		submitData = g_strconcat(submitData, (gchar *)escapeThis, "&eventSeverity=", sev, NULL);
	}

	/* Concat eventDescripton */
	if((char *)gtk_text_buffer_get_text(buffer, &startIter, &endIter, FALSE) != NULL) {
		escapeThis = curl_easy_escape(uidata->data->curl, (char *)gtk_text_buffer_get_text(buffer, &startIter, &endIter, FALSE), 0);
		submitData = g_strconcat(submitData, "&eventDescription=", (gchar *)escapeThis, NULL);
	}

	/* Concat hasVideo */			
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uidata->recordtoggle))) {
		submitData = g_strconcat(submitData, "&hasVideo=yes", NULL);
	}
	
	/* Concat GPS */
	if(!uidata->data->gpsHasFix)
		submitData = g_strconcat(submitData, "&submit=Submit", NULL);
	else {
		g_ascii_dtostr(gpslat, 10, uidata->data->gpsData->gpslat);
		g_ascii_dtostr(gpslong, 10, uidata->data->gpsData->gpslong);
		submitData = g_strconcat(submitData, "&latitude=", gpslat, "&longitude", gpslong, "&submit=Submit", NULL);
	}
	
	curl_free(escapeThis);
	/* Submit the data. URL should be read from a config file. */

	curl_easy_setopt(uidata->data->curl, CURLOPT_POSTFIELDS, submitData);
	curl_easy_setopt(uidata->data->curl, CURLOPT_URL, url);
	curl_easy_setopt(uidata->data->curl, CURLOPT_WRITEFUNCTION, discardResp);
	curl_easy_setopt(uidata->data->curl, CURLOPT_WRITEDATA, uidata);
	curl_easy_setopt(uidata->data->curl, CURLOPT_VERBOSE, 1);
	/* If we're not sending video we don't care about the headers. Also, don't
	 * create a vstream object unless the record button is toggled on. */
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uidata->recordtoggle))) {
		curl_easy_setopt(uidata->data->curl, CURLOPT_HEADERFUNCTION, getHeaders);
		curl_easy_setopt(uidata->data->curl, CURLOPT_WRITEHEADER, uidata);
		uidata->data->vstream = new_video_stream(uidata);
	}
	CURLcode rc = curl_easy_perform(uidata->data->curl);

	if(CURLE_OK != rc ) {
		logit("Unable to submit report! Error: \n    ", GTK_TEXT_VIEW(uidata->logview));
		logit((gchar *)curl_easy_strerror(rc), GTK_TEXT_VIEW(uidata->logview));
		logit("\nPlease try to submit the report again.\n", GTK_TEXT_VIEW(uidata->logview));
	}
	else
		logit("Submitted report to server sucessfully.\n", GTK_TEXT_VIEW(uidata->logview));
	

	/* If record button is toggled on */
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uidata->recordtoggle))) {
		gtk_image_set_from_stock(GTK_IMAGE(uidata->streamStatusIcon), GTK_STOCK_MEDIA_RECORD, GTK_ICON_SIZE_LARGE_TOOLBAR);
		startVideo(uidata->data->vstream, uidata);
		/* Set the stop button to sensitive so the user can stop the stream now. */
		gtk_widget_set_sensitive(uidata->stopbutton, TRUE);
	}
	
}

/* Function:	createReport
 * Return:		bool, true if successful
 * Arguments:	GtkButton*, gpointer
 *
 * This will create the report, talk to the server, and submit the report.
 * We will first need to get the status of some of the componets of the UI,
 * namely the Record Video toggle. If the device is not connected to the server
 * we immediately put up an error message and return false. Then, if connected,
 * we disable certian UI componets like the submit and clear buttons. We create
 * a thread and talk to the server: telling it that we're submitting a report,
 * and giving it the report contents. This is threaded because from this point
 * on the execution of sending the report should be non blocking. If the report 
 * contains video, we set up a vstream object and tell it to start streaming to
 * the server.
 *
 */
 gboolean createReport(GtkButton *button, gpointer user_data) {
	AppUIData* uidata = user_data;
	/*GError *error = NULL;*/
	logit("Setting up report.\n", GTK_TEXT_VIEW(uidata->logview));
	
	if(!uidata->data->connectedToServer) {
		/* TODO: Error message here. User is not connected to server */
		return FALSE;
	}
	
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uidata->recordtoggle))) {
		gtk_widget_set_sensitive(uidata->stopbutton, FALSE);
		gtk_widget_set_sensitive(uidata->submitbutton, FALSE);
		gtk_widget_set_sensitive(uidata->categorycombo, FALSE);
		gtk_widget_set_sensitive(uidata->severitycombo, FALSE);
		gtk_widget_set_sensitive(uidata->recordtoggle, FALSE);
		gtk_widget_set_sensitive(uidata->clearbutton, FALSE);
		gtk_widget_set_sensitive(uidata->detailsview, FALSE);
	}
	
	/* Create a thread to communicate with the server and do the video
	if(!g_thread_create(submitReport, uidata, TRUE, &error)) {
		g_critical("Failed to create video thread: %s\n", error->message);
		return FALSE;
	}
	 TODO: I took this out of a thread because it was causing some very strange bugs. */
	submitReport(uidata);
	
	return TRUE;
}

/* Function:	stopButton
 * Return:		void
 * Arguments:	GtkButton*, gpointer
 *
 * stopButton is executed when the Stop button in the UI is pressed. It needs
 * to clean up everything that createReport did, namely stopping the video
 * stream and freeing up any memory used. This function is never called if the
 * user didn't check the streaming option on the UI, so we can assume that a
 * stream is happening.
 */
int stopButton(GtkButton *button, gpointer user_data) {
 	AppUIData* uidata = user_data; 	
 	char *submitData = "deviceID=";
 	char *url = "http://";
 	gchar devid[5];
 	 
 	g_ascii_dtostr(devid, 5, (gdouble)uidata->data->config->DeviceID);	
 	submitData = g_strconcat(submitData, devid, NULL);
 	url = g_strconcat(url, uidata->data->config->Server, "/eel/stop.php", NULL);
 	
 	/* First submit to stop.php to tell the server to stop the stuff */
 	
 	curl_easy_setopt(uidata->data->curl, CURLOPT_POSTFIELDS, submitData);
	curl_easy_setopt(uidata->data->curl, CURLOPT_URL, url);
/*	curl_easy_setopt(uidata->data->curl, CURLOPT_WRITEFUNCTION, discardResp);
	curl_easy_setopt(uidata->data->curl, CURLOPT_WRITEDATA, uidata);*/
	CURLcode rc = curl_easy_perform(uidata->data->curl);

	if(CURLE_OK != rc ) {
		logit("Could not stop the video! Error: \n    ", GTK_TEXT_VIEW(uidata->logview));
		logit((gchar *)curl_easy_strerror(rc), GTK_TEXT_VIEW(uidata->logview));
		logit("\nPlease try to stop again.\n", GTK_TEXT_VIEW(uidata->logview));
		return -1;
	}
	else
		logit("Notified server of our stop request.\n", GTK_TEXT_VIEW(uidata->logview));


	/* Next, let's actually stop the video */
	gst_element_set_state(GST_ELEMENT(uidata->data->vstream->pipeline), GST_STATE_NULL);
	logit("Stopped video stream.\n", GTK_TEXT_VIEW(uidata->logview));
 	destroy_video_stream(uidata->data->vstream);

	gtk_widget_set_sensitive(uidata->submitbutton, TRUE);
	gtk_widget_set_sensitive(uidata->stopbutton, FALSE);
	gtk_widget_set_sensitive(uidata->categorycombo, TRUE);
	gtk_widget_set_sensitive(uidata->severitycombo, TRUE);
	gtk_widget_set_sensitive(uidata->recordtoggle, TRUE);
	gtk_widget_set_sensitive(uidata->clearbutton, TRUE);
	gtk_widget_set_sensitive(uidata->detailsview, TRUE);
	
	gtk_image_set_from_stock(GTK_IMAGE(uidata->streamStatusIcon), GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_LARGE_TOOLBAR);
	
	return 0; 	
}

/* Function:	logit
 * Return:		void
 * Arguments:	char*, GtkWidget*
 *
 * Log takes the string specified, appends it to the
 * GtkWidget specified (which we assume is a GtkTextView,
 * and scrolls the GtkTextView to the bottom of the screen
 */
void logit(char *string, GtkTextView *textview) {
	GtkTextIter iter;
	GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);

	buffer = gtk_text_view_get_buffer(textview);
	gtk_text_buffer_get_end_iter(buffer, &iter);

	gtk_text_buffer_insert(buffer, &iter, string, -1);

	gtk_text_view_scroll_mark_onscreen(textview, gtk_text_buffer_get_insert(buffer));	
}

/* Funcation:	update_clock
 * Return:		void
 * Arguments:	gpointer cb_date
 *
 * Called when GTK is idle. Updates the clock display.
 */
 gboolean update_clock( gpointer cb_data ) {
	AppUIData *ui;
	ui = cb_data;
	
	time_t dtime;
	struct tm *time_info;
	time( &dtime );
	time_info = localtime( &dtime ); 

	char *temp;
	temp = (char *) g_malloc( 128 );
	
	strftime( temp, 128, " %a %d %b %y %I:%M:%S %p", time_info );
	
	gtk_label_set_text( GTK_LABEL( ui->timeLabel ), temp );
	
	g_free( temp );
	return TRUE;
}
